package bsh.asm.acessor;

import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;

import bsh.CompilationState;
import bsh.exception.CompilationFailedException;
import bsh.meta.TypeWrapper;

/**
 * {@link ObjectAccessor} to access external variables - variables that belong to an outer class or local variables from the enclosing scope
 * 
 * @author takeshi
 */
public class ExternalVariableObjectAccessor implements ObjectAccessor {

    private final ObjectAccessor container;

    private final String fieldName;

    private final TypeWrapper type;

    /**
     * C'tor
     * 
     * @param container the container that has the accessor for the field
     * @param fieldName the field name
     * @param type the type
     */
    public ExternalVariableObjectAccessor(ObjectAccessor container, String fieldName, TypeWrapper type) {
        super();
        this.container = container;
        this.fieldName = fieldName;
        this.type = type;
    }

    /**
     * {@inheritDoc}
     */
    public TypeWrapper type() {
        return this.type;
    }

    /**
     * {@inheritDoc}
     */
    public boolean readOnly() {
        return true;
    }

    /**
     * {@inheritDoc}
     */
    public void loadObject(MethodVisitor visitor, CompilationState state) throws CompilationFailedException {
        int size = state.getStackSize();
        container.loadObject(visitor, state);
        state.assertStackSize(1 + size);
        state.popStack();
        visitor.visitFieldInsn(Opcodes.GETFIELD, container.type().internalName(), this.fieldName, this.type().typeDescription());
        state.pushStack(this.type());
    }

    /**
     * {@inheritDoc}
     */
    public void preStore(MethodVisitor visitor, CompilationState state) {
        throw new IllegalStateException();
    }

    /**
     * {@inheritDoc}
     */
    public void storeObject(MethodVisitor visitor, CompilationState state) {
        throw new IllegalStateException();
    }

    /**
     * @return the container
     */
    public ObjectAccessor getContainer() {
        return container;
    }

    /**
     * @return the fieldName
     */
    public String getFieldName() {
        return fieldName;
    }

    /**
     * @return the type
     */
    public TypeWrapper getType() {
        return type;
    }

}
